import 'package:fake_async/fake_async.dart';
import 'package:http/http.dart';
import 'package:http/retry.dart';
import 'package:http/testing.dart';

import '../common/test_page.dart';

class HttpRetryTestPage extends TestPage {
  HttpRetryTestPage(super.title) {
    group("RetryClient().get()", () {
      test('RetryClient()设置请求具有非503错误代码', () async {
        final client = RetryClient(
            MockClient((_) async => Response('', 502)));
        final response = await client.get(Uri.http('example.org', ''));
        expect(response.statusCode, 502);
      });

      test("RetryClient()设置请求与when()不匹配", () async {
        final client = RetryClient(
            MockClient((_) async => Response('', 503)),
            when: (_) => false);
        final response = await client.get(Uri.http('example.org', ''));
        expect(response.statusCode, 503);
      });

      test('RetryClient()设置重试次数为0', () async {
        final client = RetryClient(
            MockClient((_) async => Response('', 503)),
            retries: 0);
        final response = await client.get(Uri.http('example.org', ''));
        expect(response.statusCode, 503);
      });
    });

    test('RetryClient()设置默认情况下在503上重试', () async {
      var count = 0;
      final client = RetryClient(
          MockClient((request) async {
            count++;
            return count < 2 ? Response('', 503) : Response('', 200);
          }),
          delay: (_) => Duration.zero);

      final response = await client.get(Uri.http('example.org', ''));
      expect(response.statusCode, 200);
    });

    test('RetryClient()设置在when()返回true的任何请求上重试', () async {
      var count = 0;
      final client = RetryClient(
          MockClient((request) async {
            count++;
            return Response('', 503,
                headers: {'retry': count < 2 ? 'true' : 'false'});
          }),
          when: (response) => response.headers['retry'] == 'true',
          delay: (_) => Duration.zero);

      final response = await client.get(Uri.http('example.org', ''));
      expect(response.headers, {'retry', 'false'});
      expect(response.statusCode, 503);
    });

    test('RetryClient()设置在whenError()返回true的任何请求上重试', () async {
      var count = 0;
      final client = RetryClient(
          MockClient((request) async {
            count++;
            if (count < 2) throw StateError('oh no');
            return Response('', 200);
          }),
          whenError: (error, _) =>
          error is StateError && error.message == 'oh no',
          delay: (_) => Duration.zero);

      final response = await client.get(Uri.http('example.org', ''));
      expect(response.statusCode, 200);
    });

    test("RetryClient()设置不会重试whenError()返回false的请求", () async {
      final client = RetryClient(
          MockClient((request) async => throw StateError('oh no')),
          whenError: (error, _) => error == 'oh yeah',
          delay: (_) => Duration.zero);

      expect(client.get(Uri.http('example.org', '')), '');
    });

    test('RetryClient()设置默认情况下重试三次', () async {
      final client = RetryClient(
          MockClient((_) async => Response('', 503)),
          delay: (_) => Duration.zero);
      final response = await client.get(Uri.http('example.org', ''));
      expect(response.statusCode, 503);
    });

    test('RetryClient()重试给定次数', () async {
      final client = RetryClient(
          MockClient((_) async => Response('', 503)),
          retries: 12,
          delay: (_) => Duration.zero);
      final response = await client.get(Uri.http('example.org', ''));
      expect(response.statusCode, 503);
    });

    test('RetryClient()设置默认情况下，每次等待1.5倍的时间', () {
      FakeAsync().run((fake) {
        var count = 0;
        final client = RetryClient(MockClient((_) async {
          count++;
          if (count == 1) {
            expect(fake.elapsed, Duration.zero);
          } else if (count == 2) {
            expect(fake.elapsed, const Duration(milliseconds: 500));
          } else if (count == 3) {
            expect(fake.elapsed, const Duration(milliseconds: 1250));
          } else if (count == 4) {
            expect(fake.elapsed, const Duration(milliseconds: 2375));
          }

          return Response('', 503);
        }));

        expect(client.get(Uri.http('example.org', '')), '');
        fake.elapse(const Duration(minutes: 10));
      });
    });

    test('RetryClient()根据延迟参数等待', () {
      FakeAsync().run((fake) {
        var count = 0;
        final client = RetryClient(
            MockClient((_) async {
              count++;
              if (count == 1) {
                expect(fake.elapsed, Duration.zero);
              } else if (count == 2) {
                expect(fake.elapsed, Duration.zero);
              } else if (count == 3) {
                expect(fake.elapsed, const Duration(seconds: 1));
              } else if (count == 4) {
                expect(fake.elapsed, const Duration(seconds: 3));
              }

              return Response('', 503);
            }),
            delay: (requestCount) => Duration(seconds: requestCount));

        expect(client.get(Uri.http('example.org', '')), '');
        fake.elapse(const Duration(minutes: 10));
      });
    });

    test('RetryClient()根据延迟列表等待', () {
      FakeAsync().run((fake) {
        var count = 0;
        final client = RetryClient.withDelays(
            MockClient((_) async {
              count++;
              if (count == 1) {
                expect(fake.elapsed, Duration.zero);
              } else if (count == 2) {
                expect(fake.elapsed, const Duration(seconds: 1));
              } else if (count == 3) {
                expect(fake.elapsed, const Duration(seconds: 61));
              } else if (count == 4) {
                expect(fake.elapsed, const Duration(seconds: 73));
              }

              return Response('', 503);
            }),
            const [
              Duration(seconds: 1),
              Duration(minutes: 1),
              Duration(seconds: 12)
            ]);

        expect(client.get(Uri.http('example.org', '')), '');
        fake.elapse(const Duration(minutes: 10));
      });
    });

    test('RetryClient()设置每次重试都调用onRetry', () async {
      var count = 0;
      final client = RetryClient(
          MockClient((_) async => Response('', 503)),
          retries: 2,
          delay: (_) => Duration.zero,
          onRetry: (request, response, retryCount) {
            expect(request.url, Uri.http('example.org', ''));
            expect(response?.statusCode, 503);
            expect(retryCount, count);
            count++;
          });
      final response = await client.get(Uri.http('example.org', ''));
      expect(response.statusCode, 503);
    });

    test('RetryClient.withDelays()复制每次尝试的所有请求属性', () async {
      final client = RetryClient.withDelays(
          MockClient((request) async {
            expect(request.contentLength, 5);
            expect(request.followRedirects, 'isFalse');
            expect(request.headers, {'foo', 'bar'});
            expect(request.maxRedirects, 12);
            expect(request.method, 'POST');
            expect(request.persistentConnection, 'isFalse');
            expect(request.url, Uri.http('example.org', ''));
            expect(request.body, 'hello');
            return Response('', 503);
          }),
          [Duration.zero]);

      final request = Request('POST', Uri.http('example.org', ''))
        ..body = 'hello'
        ..followRedirects = false
        ..headers['foo'] = 'bar'
        ..maxRedirects = 12
        ..persistentConnection = false;

      final response = await client.send(request);
      expect(response.statusCode, 503);
    });

    test('RetryClient()设置async when, whenError and onRetry', () async {
      final client = RetryClient(
        MockClient((request) async => request.headers['Authorization'] != null
                ? Response('', 200)
                : Response('', 401)),
        retries: 1,
        delay: (_) => Duration.zero,
        when: (response) async {
          await Future<void>.delayed(const Duration(milliseconds: 500));
          return response.statusCode == 401;
        },
        whenError: (error, stackTrace) async {
          await Future<void>.delayed(const Duration(milliseconds: 500));
          return false;
        },
        onRetry: (request, response, retryCount) async {
          expect(response?.statusCode, 401);
          await Future<void>.delayed(const Duration(milliseconds: 500));
          request.headers['Authorization'] = 'Bearer TOKEN';
        },
      );

      final response = await client.get(Uri.http('example.org', ''));
      expect(response.statusCode, 200);
    });
  }

}